package com.hhuhh.smarthome.socket.udp;

import java.net.InetSocketAddress;
import java.security.PrivateKey;
import java.security.PublicKey;

import org.apache.commons.logging.Log;
import org.apache.mina.core.future.CloseFuture;
import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.core.future.IoFuture;
import org.apache.mina.core.future.IoFutureListener;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.transport.socket.nio.NioDatagramAcceptor;
import org.apache.mina.transport.socket.nio.NioDatagramConnector;

import com.hhuhh.smarthome.socket.AcceptorCallback;
import com.hhuhh.smarthome.socket.IoConstants;
import com.hhuhh.smarthome.socket.model.ResponseDataType;
import com.hhuhh.smarthome.socket.support.ByteCodecFactory;
import com.hhuhh.smarthome.socket.support.IoFilterHolder;
import com.hhuhh.smarthome.utils.ValidatorUtils;

/**
 * @author John zhang
 * @version 0.1
 */
public class UdpClient {
	
	private static final String tag = UdpClient.class.getSimpleName ();
	
	private NioDatagramConnector connector;
	private IoSession session = null;
	private ConnectFuture connectFuture;
	private String serverAddress;
	private int port;
	private UdpClientIoHandler handler;

	public UdpClient( String serverAddress, int port ) {
		if( !ValidatorUtils.isDomain ( serverAddress ) || !ValidatorUtils.isIp ( serverAddress ) ) {
			throw new IllegalArgumentException ( "serverAddress is a incorrect expression!" );
		}
		if( port <= 0 ) {
			throw new IllegalArgumentException ( "port must > 0" );
		}
		if( connector == null ) {
			
		}
		this.serverAddress = serverAddress;
		this.port = port;
	}
	
	/**
	 * 构造一个简单的Udp客户端
	 * @return
	 */
	private NioDatagramConnector buildSimpleConnector() {
		NioDatagramConnector connector = new NioDatagramConnector ();

		connector.getSessionConfig ().setMinReadBufferSize ( 64 * 1024 );
		connector.getSessionConfig ().setMaxReadBufferSize ( 1024 * 1024 );
		connector.getSessionConfig ().setReadBufferSize ( 1024 * 1024 );
		connector.getSessionConfig ().setReceiveBufferSize ( 1024 * 1024 );
		
		connector.getSessionConfig ().setIdleTime ( IdleStatus.BOTH_IDLE, 30 );

		connector.getFilterChain ().addLast ( "codec", new ProtocolCodecFilter ( new ByteCodecFactory() ) );
		connector.getFilterChain ().addLast ( "executor", IoFilterHolder.executorFilter ( 10 ) );
		connector.getFilterChain ().addLast ( "logger",IoFilterHolder.loggingFilter () );
		handler = new UdpClientIoHandler ();
		connector.setHandler ( handler );
		return connector;
	}
	
	public boolean connect() {
		return connect( null );
	}
	
	public boolean connect( NioDatagramConnector clientConnector ) {
		if( connector == null ) {
			this.connector = buildSimpleConnector ();
		} else {
			this.connector = clientConnector;
		}
		try {
			connectFuture = connector.connect ( new InetSocketAddress ( serverAddress, port ) );
			// 使connector处于等待不中断状态
			connectFuture.awaitUninterruptibly ();
			session = connectFuture.getSession ();
			session.getConfig ().setUseReadOperation ( true );
			session.getCloseFuture ().addListener ( new IoFutureListener<IoFuture>() {
				@Override
				public void operationComplete( IoFuture future ) {
					if( future.getSession ().isClosing () )
						connector.dispose ();
				}
			} );
		} catch ( Exception e ) {
			e.printStackTrace ();
		}
		return connectFuture.isConnected ();
	}
	
	/**
	 * 发送数据
	 * @param action 请求标识
	 * @param message
	 */
	public void sent( String action, String message ) {
		if( IoConstants.DEBUG ) {
			System.out.println( String.format ( "client请求服务端响应标识action=%s", action ) );
		}
		session.setAttribute ( IoConstants.REQUEST_ACTION_KEY, action );
		
		session.write ( action + message );
	}
	
	/**
	 * 发送数据
	 * @param action 请求标识
	 * @param message
	 */
	public void sent( String action, byte[] message ) {
		session.setAttribute ( IoConstants.REQUEST_ACTION_KEY, action );
		
		session.write ( message );
	}
	
	public boolean closeSession() {
		CloseFuture future = session.getCloseFuture ();
		future.awaitUninterruptibly ();
		session.close ( true );
		return future.isClosed ();
	}
	
	public IoSession getSession() {
		return session;
	}

	public boolean isConnected() {
		if( session == null || connectFuture == null ) {
			return false;
		}
		return session.isConnected () && connectFuture.isConnected ();
	}
	
	public void setUseRSA( boolean val) {
		setUseRSA( val, null, null );
	}
	
	public void setUseRSA( boolean val, PublicKey pubKey, PrivateKey priKey ) {
		handler.setUseRSA ( val, pubKey, priKey );
	}
	
	/**
	 * 添加请求响应的回调函数
	 * @param key
	 * @param callback
	 */
	public void addAcceptorCallback( String key, AcceptorCallback<? extends ResponseDataType> callback ) {
		handler.addCallback ( key, callback );
	}
	
}